#include "elinputnumber.h"
#include <QDoubleSpinBox>
#include <QPaintEvent>
#include <QPainter>
#include <QTimer>
#include "../basic/elicon.h"
#include "../basic/eltheme.h"

const int ElInputNumber::Editor_Width = 80;
const int ElInputNumber::Button_Width = 40;
const int ElInputNumber::Init_Height = 32;
const int ElInputNumber::Padding_Lr = 7;
const int ElInputNumber::Padding_Tb = 3;

ElInputNumber::ElInputNumber(QWidget *parent)
    : ElComponent{parent}
{
    resize(Editor_Width + 2 * Padding_Lr + 2 * Button_Width, Init_Height);
    _doubleInput = new QDoubleSpinBox(this);
    auto font = _doubleInput->font();
    font.setPointSize(10);
    _doubleInput->setFont(font);
    _doubleInput->setFrame(false);
    _doubleInput->setButtonSymbols(QAbstractSpinBox::NoButtons);
    _doubleInput->setDecimals(0);
    _doubleInput->setMinimum(-99);
    _doubleInput->setMaximum(99);
    _doubleInput->setAlignment(Qt::AlignCenter);
    _doubleInput->resize(Editor_Width, Init_Height - 2 * Padding_Tb);
    _doubleInput->move(Button_Width + Padding_Lr, Padding_Tb);
    _doubleInput->installEventFilter(this);
    connect(_doubleInput, &QDoubleSpinBox::valueChanged, this, &ElInputNumber::judgeValue);

    _buttons[0] = new ElInputNumberButton(ElInputNumberButton::Left, this);
    auto drawRect = getDrawRect();
    _buttons[0]->move(drawRect.topLeft().toPoint());
    _buttons[1] = new ElInputNumberButton(ElInputNumberButton::Right, this);
    _buttons[1]->move(drawRect.right() - _buttons[1]->width(), drawRect.top());
    connect(_buttons[0], &ElInputNumberButton::clicked, this, &ElInputNumber::minus);
    connect(_buttons[1], &ElInputNumberButton::clicked, this, &ElInputNumber::plus);

    emit _doubleInput->valueChanged(_doubleInput->value());
    setAttribute(Qt::WA_Hover);
    initPalette();
}

bool ElInputNumber::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        _palette.toggleColorGroup(isEnabled() ? ElPalette::Normal : ElPalette::Disabled);
        update();
    }
    return ElComponent::event(e);
}

bool ElInputNumber::eventFilter(QObject *o, QEvent *e)
{
    if (o == _doubleInput) {
        if (e->type() == QEvent::FocusIn) {
            _palette.toggleColorGroup(ElPalette::Pressed);
            update();
        } else if (e->type() == QEvent::FocusOut) {
            _palette.toggleColorGroup(ElPalette::Normal);
            update();
        }
    }
    return ElComponent::eventFilter(o, e);
}

void ElInputNumber::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    painter.setBrush(_palette.color(ElPalette::Background));
    painter.setPen(_palette.color(ElPalette::Border));
    painter.drawRoundedRect(getDrawRect(), height() / 6.0, height() / 6.0);
    bool isNone = _position == None;
    // draw lines between input and buttons
    if (!isNone) {
        painter.setPen(_palette.color(ElPalette::Normal, ElPalette::Border));
        if (_position == LeftRight) {
            auto left = _buttons[0]->rect().right() + 2;
            painter.drawLine(left, 0, left, height());
            painter.fillRect(QRect(left - 5, 2, 4, height() - 4),
                             _buttons[0]->palette().color(ElPalette::Normal, ElPalette::Background));
            left = _buttons[1]->x();
            painter.drawLine(left, 0, left, height());
            painter.fillRect(QRect(left, 2, 4, height() - 4),
                             _buttons[1]->palette().color(ElPalette::Normal, ElPalette::Background));
        } else { // TopBottom
            auto drawRect = getDrawRect();
            auto left = drawRect.right() - Button_Width;
            painter.drawLine(left, 0, left, height());
            auto top = drawRect.top() + Init_Height / 2 - 1;
            painter.drawLine(left, top, drawRect.right(), top);
        }
    }
    e->accept();
}

void ElInputNumber::enterEvent(QEnterEvent *e)
{
    if (!isEnabled() || _palette.currentColorGroup() == ElPalette::Pressed) {
        e->ignore();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Entered);
    e->accept();
}

void ElInputNumber::leaveEvent(QEvent *e)
{
    if (!isEnabled() || _palette.currentColorGroup() == ElPalette::Pressed) {
        e->ignore();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Normal);
    e->accept();
}

void ElInputNumber::initPalette()
{
    _palette.setColor(ElPalette::Border, ThemeColor::textDisabledColor);
    _palette.setColor(ElPalette::Entered, ElPalette::Border, ThemeColor::infoColor);
    // Pressed means focusing in
    _palette.setColor(ElPalette::Pressed, ElPalette::Border, ThemeColor::primaryColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Background, ThemeColor::inputDisabledColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Border, ThemeColor::textDisabledColor);
}

ElInputNumber::ButtonPosition ElInputNumber::position() const
{
    return _position;
}

void ElInputNumber::setPosition(ButtonPosition position)
{
    if (_position == position)
        return;
    _position = position;
    delete _buttons[0];
    _buttons[0] = nullptr;
    delete _buttons[1];
    _buttons[1] = nullptr;
    if (_position == LeftRight) {
        resize(Editor_Width + 2 * Padding_Lr + 2 * Button_Width, Init_Height);
        _buttons[0] = new ElInputNumberButton(ElInputNumberButton::Left, this);
        auto drawRect = getDrawRect();
        _buttons[0]->move(drawRect.topLeft().toPoint());
        _buttons[1] = new ElInputNumberButton(ElInputNumberButton::Right, this);
        _buttons[1]->move(drawRect.right() - _buttons[1]->width(), drawRect.top());
        _doubleInput->move(Button_Width + Padding_Lr, Padding_Tb);
    } else if (_position == TopBottom) {
        resize(Editor_Width + 2 * Padding_Lr + Button_Width, Init_Height);
        _buttons[0] = new ElInputNumberButton(ElInputNumberButton::Bottom, this);
        auto drawRect = getDrawRect();
        _buttons[0]->move(drawRect.right() - _buttons[0]->width(), drawRect.top());
        _buttons[1] = new ElInputNumberButton(ElInputNumberButton::Top, this);
        _buttons[1]->move(drawRect.right() - _buttons[1]->width(),
                          drawRect.bottom() - _buttons[1]->height());
        _doubleInput->move(Padding_Lr, Padding_Tb);
    } else {
        resize(Editor_Width + 2 * Padding_Lr, Init_Height);
        _doubleInput->move(Padding_Lr, Padding_Tb);
    }
    bool isNone = _position == None;
    if (!isNone) {
        connect(_buttons[0], &ElInputNumberButton::clicked, this, &ElInputNumber::minus);
        connect(_buttons[1], &ElInputNumberButton::clicked, this, &ElInputNumber::plus);
    }
}

void ElInputNumber::plus()
{
    _doubleInput->setValue(_doubleInput->value() + _doubleInput->singleStep());
}

void ElInputNumber::minus()
{
    _doubleInput->setValue(_doubleInput->value() - _doubleInput->singleStep());
}

void ElInputNumber::judgeValue(double val)
{
    if (_position == None) // no buttons in this case
        return;
    _buttons[0]->setEnabled(true);
    _buttons[1]->setEnabled(true);
    if (val <= _doubleInput->minimum()) {
        _doubleInput->setValue(_doubleInput->minimum());
        _buttons[0]->setDisabled(true);
    } else if (val >= _doubleInput->maximum()) {
        _doubleInput->setValue(_doubleInput->maximum());
        _buttons[1]->setDisabled(true);
    }
}

ElInputNumberButton::ElInputNumberButton(Position position, ElInputNumber *parent)
    : ElComponent{parent}
    , _position(position)
{
    _icon = new ElIcon((_position == Left || _position == Bottom) ? ":/icons/minus.svg"
                                                                  : ":/icons/plus.svg",
                       this);
    if (_position == Left || _position == Right) {
        resize(ElInputNumber::Button_Width, ElInputNumber::Init_Height - 2);
        _icon->resize(16, 16);
    } else {
        resize(ElInputNumber::Button_Width, ElInputNumber::Init_Height / 2 - 1);
        _icon->resize(12, 12);
    }
    _icon->move((width() - _icon->width()) / 2, (height() - _icon->height()) / 2);
    _icon->show();
    _delayTimer = new QTimer(this);
    _delayTimer->setSingleShot(true);
    _delayTimer->setTimerType(Qt::CoarseTimer);
    connect(_delayTimer, &QTimer::timeout, this, [=] {
        if (_pressed)
            _timerId = startTimer(_repeatInteval, Qt::CoarseTimer);
    });
    setAttribute(Qt::WA_Hover);
    setCursor(Qt::PointingHandCursor);
    initPalette();
}

bool ElInputNumberButton::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        if (!isEnabled())
            resetTimer();
        _palette.toggleColorGroup(isEnabled() ? ElPalette::Normal : ElPalette::Disabled);
        update();
    }
    return ElComponent::event(e);
}

void ElInputNumberButton::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    painter.setBrush(_palette.color(ElPalette::Normal, ElPalette::Background));
    painter.setPen(Qt::NoPen);
    painter.drawRoundedRect(getDrawRect(), height() / 8.0, height() / 8.0);
    _icon->fillBackground(_palette.color(ElPalette::Foreground));
    e->accept();
}

void ElInputNumberButton::initPalette()
{
    _palette.setColor(ElPalette::Background, ThemeColor::inputDisabledColor);
    _palette.setColor(ElPalette::Foreground, ThemeColor::textPrimaryColor);
    _palette.setColor(ElPalette::Entered, ElPalette::Foreground, ThemeColor::primaryColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Foreground, ThemeColor::textDisabledColor);
}

void ElInputNumberButton::resetTimer()
{
    if (_timerId >= 0) {
        killTimer(_timerId);
        _timerId = -1;
    }
}

void ElInputNumberButton::enterEvent(QEnterEvent *e)
{
    if (!isEnabled()) {
        e->ignore();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Entered);
    e->accept();
}

void ElInputNumberButton::leaveEvent(QEvent *e)
{
    if (!isEnabled()) {
        e->ignore();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Normal);
    e->accept();
}

void ElInputNumberButton::mousePressEvent(QMouseEvent *e)
{
    _pressed = true;
    if (_delayTimer->isActive()) { // debounce
        _delayTimer->stop();
    }
    _delayTimer->start(_repeatDelay);
    e->accept();
}

void ElInputNumberButton::mouseReleaseEvent(QMouseEvent *e)
{
    _pressed = false;
    resetTimer();
    auto pos = e->pos();
    if (pos.x() >= 0 && pos.x() <= width() && pos.y() >= 0 && pos.y() <= height())
        emit clicked(*this);
    e->accept();
}

void ElInputNumberButton::timerEvent(QTimerEvent *e)
{
    emit clicked(*this);
    e->accept();
}
